﻿using System;
using System.Collections.Generic;
using System.Text;

namespace AMS.DataAccess.Filter
{
    /// <summary>
    /// Provide filter for datetime field.
    /// Unlike other filters, the DateTimeFilterField.ToString() return format
    /// of the datetime constraint, like:
    /// (1). DATEDIFF(SECOND, {0}, '1949-10-01') = 0
    /// (2). DATEDIFF(DAY, {0}, '1949-10-01') &gt;= 0 AND DATEDIFF(DAY, {1}, '1949-10-02') &lt;= 0
    /// A call to string.Format(format, columnName) is essencial to
    /// create a condition expression.
    /// </summary>
    /// <remarks>按照目前的实现，只适用于Sql Server</remarks>
    public class DateTimeFilterField : FilterField
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <param name="type"></param>
        public DateTimeFilterField(DateTime value, DateTimeType type)
            :this(value, type, SqlOperations.Equals)
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <param name="type"></param>
        /// <param name="operation"></param>
        public DateTimeFilterField(DateTime value, DateTimeType type, SqlOperations operation)
        {
            StartValue = value;
            DateTimeType = type;
            Operation = operation;
            isBetween = false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <param name="type"></param>
        public DateTimeFilterField(DateTime start, DateTime end, DateTimeType type)
            :this(start, end, type, true, true)
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <param name="type"></param>
        /// <param name="includeStart"></param>
        /// <param name="includeEnd"></param>
        public DateTimeFilterField(DateTime start, DateTime end, DateTimeType type, bool includeStart, bool includeEnd)
        {
            StartValue = start;
            EndValue = end;
            DateTimeType = type;
            IncludeStartDateTime = includeStart;
            IncludeEndDateTime = includeEnd;
            isBetween = true;
        }

        #region 字段和属性
        private bool isBetween;

        /// <summary>
        /// 
        /// </summary>
        public DateTime StartValue { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        public DateTime EndValue { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        public DateTimeType DateTimeType { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        public SqlOperations Operation { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        public bool IncludeStartDateTime { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        public bool IncludeEndDateTime { get; private set; }
        #endregion

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            if (isBetween)
            {
                return BuildBetweenToString();
            }
            else
            {
                return BuildToString();
            }
        }

        private string BuildToString()
        {
            StringBuilder content = new StringBuilder();
            bool compareTime = (DateTimeType == DateTimeType.DateTime);
            if (Operation == SqlOperations.IsNotNull || Operation == SqlOperations.IsNull)
            {
                content.Append("{0}");
                content.Append(SqlEnumUtils.NumberOperationsToString(Operation));
            }
            else
            {
                content.Append("DATEDIFF(");
                content.Append(compareTime ? "SECOND" : "DAY");
                content.Append(", '");
                content.Append(StartValue.ToString(compareTime ? "yyyy-MM-dd HH:mm:ss" : "yyyy-MM-dd"));
                content.Append("', {0})");
                content.Append(SqlEnumUtils.NumberOperationsToString(Operation));
                content.Append("0");
            }

            return content.ToString();
        }

        private string BuildBetweenToString()
        {
            StringBuilder content = new StringBuilder();
            bool compareTime = (DateTimeType == DateTimeType.DateTime);
            SqlOperations littleEndOp = IncludeStartDateTime? SqlOperations.LargerThanOrEquals : SqlOperations.LargerThan;
            SqlOperations largerEndOp = IncludeEndDateTime ? SqlOperations.LessThanOrEquals : SqlOperations.LessThan;

            content.Append("DATEDIFF(");
            content.Append(compareTime ? "SECOND" : "DAY");
            content.Append(", '");
            content.Append(StartValue.ToString(compareTime ? "yyyy-MM-dd HH:mm:ss" : "yyyy-MM-dd"));
            content.Append("', {0})");
            content.Append(SqlEnumUtils.NumberOperationsToString(littleEndOp));
            content.Append("0");

            content.Append(" AND ");

            content.Append("DATEDIFF(");
            content.Append(compareTime ? "SECOND" : "DAY");
            content.Append(", '");
            content.Append(EndValue.ToString(compareTime ? "yyyy-MM-dd HH:mm:ss" : "yyyy-MM-dd"));
            content.Append("', {1})");
            content.Append(SqlEnumUtils.NumberOperationsToString(largerEndOp));
            content.Append("0");

            return content.ToString();
        }
    }
}
